package controller

import (
	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
	"gorm.io/gorm"
	"net/http"
	"shorturl/global"
	"shorturl/model"
	"shorturl/utils"
	"time"
)

type AccessController struct {
	BaseController
}

// Index 获取链接列表
func (app AccessController) Index(c *gin.Context) {
	ip := c.Query("ip")
	linkId := c.Query("link_id")
	referer := c.Query("referer")
	domain := c.Query("domain")

	// 创建时间 范围
	startDate := c.Query("create_time_start")
	endDate := c.Query("create_time_end")

	pageNo := c.DefaultQuery("pageNo", "1")
	pageSize := c.DefaultQuery("pageSize", "10")

	// 把params["page"]转换成int
	page := utils.StrToInt(pageNo)
	size := utils.StrToInt(pageSize)

	if page <= 0 {
		page = 1
	}

	if size <= 0 {
		size = 10
	}

	if size > 100 {
		size = 100
	}

	var accessLog []model.AccessLog

	var total int64

	query := global.Db.Model(&accessLog).Debug()

	if ip != "" {
		query = query.Where("ip_address = ?", ip)
	}

	if linkId != "" {
		query = query.Where("link_id = ?", linkId)
	}

	if referer != "" {
		query = query.Where("referer LIKE ?", "%"+referer+"%")
	}

	if domain != "" {
		query = query.Where("domain = ?", domain)
	}

	if startDate != "" && endDate != "" {
		query = query.Where("access_time between ? and ?", startDate, endDate)
	}

	// 查询总记录数
	query.Count(&total)

	// 查询消息列表，增加分页
	query.Order("id desc").Offset((page - 1) * size).Limit(size).Find(&accessLog)

	var result = make(map[string]interface{})

	result["total"] = total

	result["list"] = accessLog

	app.Success(c, result)
}

// Record 记录访问数据
func (app AccessController) Record(c *gin.Context) {
	id := c.Param("id")

	if id == "" {
		logger.Error("参数错误")
		app.Error(c, "参数错误")
		return
	}

	ipAddress := c.ClientIP()
	userAgent := c.Request.UserAgent()

	// 尝试从 redis 查询
	if value, err := redis.Get(id).Result(); err != nil && value != "" {
		logger.Info("Get", zap.String("id", id), zap.String("value", value))

		// 异步记录访问日志
		go app.recordAccessLog(model.AccessLog{
			RedirectUrl: value,
		}, c, ipAddress, userAgent)

		// 异步记录点击次数 和最后访问时间
		go app.recordAccessLink(id)

		c.Redirect(http.StatusMovedPermanently, value)
		return
	}

	logger.Info("Record", zap.String("id", id))

	var link model.Link
	err := global.Db.Where("no_id = ? OR hash_id = ? OR cust_name = ?", id, id, id).First(&link).Error
	if err != nil {
		logger.Error("链接不存在", zap.Error(err))
		app.Error(c, "链接不存在")
		return
	}

	if link.State == 0 {
		logger.Error("链接已禁用")
		app.Error(c, "链接已禁用")
		return
	}

	// 写入 redis 1 天
	err = redis.Set(id, link.LongUrl, 24*time.Hour).Err()
	if err != nil {
		logger.Error("redis set error", zap.Error(err))
	} else {
		logger.Info("Set", zap.String("id", id), zap.String("value", link.LongUrl))
	}

	// 异步记录访问日志
	go app.recordAccessLog(model.AccessLog{
		LinkId:      link.Id,
		RedirectUrl: link.LongUrl,
	}, c, ipAddress, userAgent)

	// 异步记录点击次数 和最后访问时间
	go app.recordAccessLink(id)

	// 立即跳转
	c.Redirect(http.StatusMovedPermanently, link.LongUrl)
}

// recordAccessLink 是用于异步记录点击次数和最后访问时间的辅助函数
func (app AccessController) recordAccessLink(id string) {
	var link model.Link
	if err := global.Db.Where("no_id = ? OR hash_id = ? OR cust_name = ?", id, id, id).First(&link).Error; err != nil {
		logger.Error("链接不存在", zap.Error(err), zap.String("id", id))
		return
	}

	if link.State == 0 {
		logger.Error("链接已禁用")
		return
	}

	if err := global.Db.Model(&link).Where("id = ?", link.Id).Updates(map[string]interface{}{
		"click":          gorm.Expr("click + 1"),
		"last_access_at": model.LocalTime(utils.GetDateTime()),
	}).Error; err != nil {
		logger.Error("更新链接点击次数失败", zap.Error(err))
	}
}

// recordAccessLog 是用于异步记录访问日志的辅助函数
func (app AccessController) recordAccessLog(mutatingLog model.AccessLog, c *gin.Context, ipAddress string, userAgent string) {
	mutatingLog.AccessTime = model.LocalTime(utils.GetDateTime())
	mutatingLog.IpAddress = ipAddress
	mutatingLog.UserAgent = userAgent

	// 提取 Referer 中的域名
	referer := c.Request.Referer()
	mutatingLog.Referer = referer

	if referer != "" {
		parsedUrl, err := utils.ParseUrl(referer)
		if err == nil {
			mutatingLog.Domain = parsedUrl.Host
		} else {
			mutatingLog.Domain = "Unknown"
		}
	} else {
		mutatingLog.Domain = "Unknown"
	}

	mutatingLog.RequestUrl = c.Request.URL.String()
	mutatingLog.Language = c.Request.Header.Get("Accept-Language")

	mutatingLog.DeviceType = utils.GetDeviceType(userAgent)
	mutatingLog.Os = utils.GetOs(userAgent)
	mutatingLog.Browser = utils.GetBrowser(userAgent)

	response, _ := utils.GetIpInfo(ipAddress)

	//fmt.Println(response)

	if response != nil {
		mutatingLog.Country = response.Country
		mutatingLog.City = response.City
		mutatingLog.Lat = response.Lat
		mutatingLog.Lon = response.Lon
	}

	if err := global.Db.Create(&mutatingLog).Error; err != nil {
		logger.Error("记录访问数据失败", zap.Error(err))
	}
}
